#include <jni.h>
#include <string>
#include <string.h>
#include <android/log.h>

#define TAG "native-lib.cpp=="
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__)    // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)    // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__)    // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__)    // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__)    // 定义LOGF类型     extern "C"


extern "C" //按着 C 的规则去翻译相应的函数名而不是 C++ 的。
JNIEXPORT jstring JNICALL //这里JNIEXPORT和JNICALL都是JNI的关键字，表示此函数是要被JNI调用的。
Java_com_jniproject_MainActivity_stringFromJNI(
        JNIEnv *env, /* 接口指针 */
        jobject /* this  指针 */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_jniproject_MainActivity_sayHelloNdk(JNIEnv *env, jobject instance) {

    std::string abc = "my String abc";
    return env->NewStringUTF(abc.c_str());
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_jniproject_MainActivity_add(JNIEnv *env, jobject instance, jint x, jint y) {
    return x + y;
}

extern "C"
JNIEXPORT jstring JNICALL
Java_com_jniproject_MainActivity_sayHello(JNIEnv *env, jobject instance, jstring s_) {
    const char *s = env->GetStringUTFChars(s_,
                                           0);//返回指向字符串的 UTF-8 字符数组的指针。该数组在被ReleaseStringUTFChars() 释放前将一直有效
//    在调用 GetStringUTFChars 函数从 JVM 内部获取一个字符串之后，
//    JVM 内部会分配一块新的内存，用于存储源字符串的拷贝，
//    以便本地代码访问和修改。即然有内存分配，用完之后马上释放是一个编程的好习惯。
//    通过调用ReleaseStringUTFChars 函数通知 JVM 这块内存已经不使用了，你可以清除了。
//    注意：这两个函数是配对使用的，
//    用了 GetXXX 就必须调用 ReleaseXXX，而且这两个函数的命名也有规律，
//    除了前面的 Get 和 Release 之外，后面的都一样。
//    env->ReleaseStringUTFChars(s_, s);//通知虚拟机平台相关代码无需再访问 utf。utf 参数是一个指针，可利用 GetStringUTFChars() 获得
    char *fromc = "add i am from c";
    char *myStr = "  123   ";
    LOGD("the name from java is 000 %s", s);
    LOGD("the name from java is 111 %s", fromc);
    //拼接函数 结果放在第一个参数里面
//    TODO 函数拼接 执行失败
//    strcat(fromc,myStr);//运行出错
    //A/libc: Fatal signal 11 (SIGSEGV) at 0x760b7780 (code=2), thread 23773 (com.jniproject)
    LOGD("the name from java is 222 %s", fromc);
    return env->NewStringUTF(fromc);
}

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_jniproject_MainActivity_increaseArrayEles(JNIEnv *env, jobject instance,
                                                   jintArray intArray_) {
    //得到数组
    jint *intArray = env->GetIntArrayElements(intArray_, NULL);

    env->ReleaseIntArrayElements(intArray_, intArray, 0);

    //2 得到数组长度
    int i;
    for (i = 0; i < sizeof(intArray); ++i) {
        *(intArray +i)=*(intArray +i)+10;
//        *(intArray + i) += 10;
        LOGD("increase %d  and before %d", *(intArray +i), (*(intArray +i)+10));
    }

    return intArray_;
}

extern "C"
JNIEXPORT jboolean JNICALL
Java_com_jniproject_MainActivity_checkPwd(JNIEnv *env, jobject instance, jstring pwd_) {
    const char *pwd = env->GetStringUTFChars(pwd_, 0);


//    env->ReleaseStringUTFChars(pwd_, pwd);
    //服务器密码 123456
    char *origin = "123456";

    //函数比较
    int code = strcmp(origin, pwd);

    return (code == 0);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_jniproject_MainActivity_jniToJava(JNIEnv *env, jobject instance) {
    //1 得到字节解码 文件
    // jclass FindClass(const char* name)
    jclass jclazz = env->FindClass("com/jniproject/MainActivity");

    //2 得到对应的方法
    //jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
    jmethodID methodid = env->GetMethodID(jclazz,"callBackJavaHelloWorld22","(Ljava/lang/String;)V");

    //3 实例化MainActivity类
    // jobject AllocObject(jclass clazz)
    jobject jobj = (*env).AllocObject(jclazz);

    jstring jstr = env->NewStringUTF(" i am from cpp");
    //4 调用方法
    (*env).CallVoidMethod(jobj,methodid,jstr);

}

extern "C"
JNIEXPORT void JNICALL
Java_com_jniproject_MainActivity_jniToJavaStatic(JNIEnv *env, jobject instance) {
    //1 得到字节解码 文件
    // jclass FindClass(const char* name)
    jclass jclazz = env->FindClass("com/jniproject/MainActivity");

    //2 得到对应的方法
    //jmethodID GetStaticMethodID(jclass clazz, const char* name, const char* sig)
    jmethodID methodid = env->GetStaticMethodID(jclazz,"callBackJavaStatic22","()V");

    //3 调用方法
    (*env).CallStaticVoidMethod(jclazz,methodid);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_jniproject_MainActivity_jniToJavaToast(JNIEnv *env, jobject instance) {
    //1 得到字节解码 文件
    // jclass FindClass(const char* name)
    jclass jclazz = env->FindClass("com/jniproject/MainActivity");

    //2 得到对应的方法
    //jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
    jmethodID methodid = env->GetMethodID(jclazz,"callBackJavaToast22","()V");

//    //3 实例化MainActivity类
//    // jobject AllocObject(jclass clazz)
//    jobject jobj = (*env).AllocObject(jclazz);

    //4 调用方法
    (*env).CallVoidMethod(instance,methodid);

}

struct Tree{
    char name[50];
    float numbs[2];
};

extern "C"
JNIEXPORT jfloatArray JNICALL
Java_com_jniproject_MainActivity_nativeGetFloatArray(JNIEnv *env, jobject instance,jlong path) {
        Tree tree;
        strcpy( tree.name, "C tree");
        tree.numbs[0] = 1.1;
        tree.numbs[1] = 2.2;
//        jsize size = env->GetArrayLength(tree.numbs);
    //todo 如何获取结构体 中数组长度？
    //1.新建长度len数组
    jint len =2;
    jfloatArray jarr = env->NewFloatArray(len);
    //2.获取数组指针
    jfloat *arrPoint = env->GetFloatArrayElements(jarr, NULL);
    //3.赋值
    for(int i = 0; i < len; i++){
//        arrPoint[i] = i;
        arrPoint[i] = tree.numbs[i];
    }
    //4.释放资源
    env->ReleaseFloatArrayElements(jarr, arrPoint, 0);
    //5.返回数组
    return jarr;
}

int com(const void *a, const void *b){
    return *(int *)a - *(int *)b;//升序
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_jniproject_MainActivity_nativeGetFloatArray2(JNIEnv *env, jobject instance,jfloatArray arr) {
    jboolean isCp = JNI_FALSE;
    //1.获取数组指针
    jfloat* carr = env->GetFloatArrayElements(arr, &isCp);
    if(carr == NULL) {
        return 0; /* exception occurred */
    }
    //2.获取数组长度
    jsize size = env->GetArrayLength(arr);
    jfloat sum = 0;
    for(int i=0; i<size; i++) {
        carr[i] = carr[i] +10.0;
    }

    //3.释放资源
    env->ReleaseFloatArrayElements(arr, carr, 0);
    /**
 mode = 0
原始数据: 对象数组将不会被限制.
拷贝数据: 数据将会拷贝回原始数据, 同时释放拷贝数据.
mode = JNI_COMMIT (1)
原始数据: 什么都不作.
拷贝数据: 数据将会拷贝回原始数据, 不释放拷贝数据.
mode = JNI_ABORT (2)
原始数据: 对象数组将不会被限制, 之前的数据操作有效
拷贝数据: 释放拷贝数据, 之前的任何数据操作会丢弃.

作者：efan
链接：https://www.jianshu.com/p/7f6dbdfbd7fd
来源：简书
     */
    return 1;
}


extern "C"
JNIEXPORT jintArray JNICALL
Java_com_jniproject_MainActivity_nativeGetIntArray(JNIEnv *env, jobject instance) {
    //1.新建长度len数组
    jint len =2;
    jintArray jarr = env->NewIntArray(len);
    //2.获取数组指针
    jint *arr = env->GetIntArrayElements(jarr, NULL);
    //3.赋值
    int i = 0;
    for(; i < len; i++){
        arr[i] = i;
    }
    //4.释放资源
    env->ReleaseIntArrayElements(jarr, arr, 0);
    //5.返回数组
    return jarr;
}

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_jniproject_MainActivity_nativeGetIntArray2(JNIEnv *env, jobject instance,jintArray intArray) {
    jboolean isCp = JNI_FALSE;
    jint* coldata = env->GetIntArrayElements(intArray, &isCp);
    jsize size = env->GetArrayLength(intArray);
    for (int j = 0; j < size; j++) {
        coldata[j] += 10;
    }

    //将C数组种的元素拷贝到Java数组中
    env->SetIntArrayRegion(intArray, 0, size,coldata);
    return intArray;

}
